package net.yuyoo.uusession.web;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.yuyoo.uusession.core.UUSessionConfig;
import net.yuyoo.uusession.core.UUSessionServantInstance;
import net.yuyoo.uusession.core.UUSessionServantInstance.EndpointMeta;
import net.yuyoo.uusession.support.JsonUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 统一会话 过滤器
 * @author yuyoo (yuyoo4j@163.com)
 * @teme 2010-6-9 下午12:56:17
 */
public class UUSessionFilter implements Filter {
	
	private static Logger log = LoggerFactory.getLogger(UUSessionFilter.class);
	
	private String cookiePath = null;
	
	private UUSessionServantInstance servant = null;

	public void destroy() {
		servant = null;
	}

	@SuppressWarnings("unchecked")
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain fc) throws IOException, ServletException {
		
		HttpServletRequest req = (HttpServletRequest)request;
		HttpServletResponse res = (HttpServletResponse)response;
		
		Cookie[] cs = req.getCookies();
		String usc = null;
		if (null != cs) {  // 存在cookie数组, 遍历数组
			for (Cookie c : cs) { // 获取cookie值
				if ("usc".equals(c.getName())) {
					usc = c.getValue();
					break;
				}
			} 
		}
		
		String sessionId = null;
		String n = null;
		String h = null;
		String l = null;
		HttpSessionAdapter adapter = null;
		try { 
			if (null != usc) { // 存在uusession的cookie
				log.debug("before filter usc:{}", usc); 
				Map<String, String> cookie = JsonUtil.toBean(usc, Map.class);
				sessionId = cookie.get("s"); // session id;
				n = cookie.get("n"); // last servant;
				h = cookie.get("h"); // heartbeat count;
				l = cookie.get("l"); // lastAccessLife;
				Long lastServantNo = Long.valueOf(n);
				long heartbeatCount = Long.parseLong(h);
				long lastAccessLife = Long.parseLong(l);
				boolean create = isTimeout(lastServantNo, heartbeatCount, lastAccessLife); 
				adapter = new HttpSessionAdapter(sessionId, servant, req.getSession(), create);
				if (!create && needToUpdateSession(lastServantNo, heartbeatCount)) {
					adapter.refresh();
				}
			}  
			if (null == adapter) { // 没有统一会话,创建新的统一会话
				adapter = new HttpSessionAdapter(sessionId, servant, req.getSession(), true);
			}
			HttpRequestWrapper wrapper = new HttpRequestWrapper(adapter, req);
			fc.doFilter(wrapper, res);
		} catch (Throwable thr) {
			log.error("构造统一会话异常", thr);
			fc.doFilter(req, res);
		} finally {
			try {
				Map<String, String> cookie = new HashMap<String, String>();
				EndpointMeta currentServantMeta = servant.getMeta();
				cookie.put("s", adapter.getId());
				cookie.put("n", Long.toString(currentServantMeta.no));
				cookie.put("h", Long.toString(currentServantMeta.heartbeatCount));
				long la = System.currentTimeMillis() - currentServantMeta.timestamp;
				cookie.put("l", Long.toString(la));
				usc = JsonUtil.toJson(cookie);
				log.debug("after filter usc:{}", usc);
				Cookie c = new Cookie("usc", usc);
				c.setMaxAge(-1);
				if (null != cookiePath) {
					c.setPath(cookiePath);
				}
				res.addCookie(c);
			} catch (Exception ex) {
				log.error("添加统一会话cookie异常", ex);
			}
		}
	}
	
	/**
	 * 检查统一会话是否超时
	 * @param lastServantNo -- 最后一个会话服务侍候器编号
	 * @param heartbeatCount -- 最后一个会话服务侍候器心跳值
	 * @param lastAccessLife -- 会话在最后一个会话服务侍候器的最后访问时长
	 * @return -- true, 会话超时
	 */
	private boolean isTimeout(Long lastServantNo, long heartbeatCount, long lastAccessLife) {
		  
		EndpointMeta lastServantMeta = servant.getMeta(lastServantNo);
		if (null == lastServantMeta) { // 会话服务侍候器元数据不存在
			throw new IllegalArgumentException("不存在的会话服务侍候器编号:" + lastServantNo);
		}
		long deltaHeartbeat = lastServantMeta.heartbeatCount - heartbeatCount;
		if ( deltaHeartbeat > 1 || deltaHeartbeat < -1) {
			log.debug("非法的会话服务侍候器心跳值:{}, 差值:{}", heartbeatCount, deltaHeartbeat);
			return true;
		}
		if (deltaHeartbeat == 0) {
			return false;
		} else if (deltaHeartbeat == 1) {
			EndpointMeta currentServantMeta = servant.getMeta();
			long now = System.currentTimeMillis();
			long deltaTime = now - currentServantMeta.timestamp;
			return (lastAccessLife + deltaTime > currentServantMeta.sessionTimeout);
		} else { // -1
			servant.asyncUpdateEndpointMeta(lastServantNo);
			return false;
		}
	}
	
	/**
	 * 是否需要更新会话数据
	 * @param lastServantNo -- 最后一个会话服务侍候器编号
	 * @param heartbeatCount -- 最后一个会话服务侍候器心跳值
	 * @return -- true, 需要更新会话数据
	 */
	private boolean needToUpdateSession(Long lastServantNo, long heartbeatCount) {

		EndpointMeta lastServantMeta = servant.getMeta(lastServantNo);
		long deltaHeartbeat = lastServantMeta.heartbeatCount - heartbeatCount;
		return (1 == deltaHeartbeat);
	}

	/**
	 * 初始化
	 */
	public void init(FilterConfig fc) throws ServletException {
		
		String p = fc.getInitParameter("cookiePath");
		if (null != p && !"".equals(p)) {
			cookiePath = p;
		}
		
		String c = fc.getInitParameter("configFile");
		if (null != c && !"".equals(c)) {
			UUSessionConfig config = new UUSessionConfig(c);
			servant = new UUSessionServantInstance(config);
		} else {
			servant = new UUSessionServantInstance();
		}
	}
	
}
